#include <libwebsockets.h>
#include <signal.h>
#include "cJSON.h"
#include <pthread.h>
#include <stdio.h>
#include<stdlib.h>
#include <time.h>
#include <alsa/asoundlib.h>

extern int mpi_ai_record();
char record_path[64] = "/tmp/cap_out.wav";

#define MAX_PAYLOAD_SIZE  50 * 1024  //如果接收的数据很多时，可以把这设置大，比如音频以base64回过来的时候，这儿设置500*1024，才可能能一次性接收，不然会分好多次接收，会导致解析麻烦

static struct lws_context_creation_info ctx_info = { 0 };
static struct lws_context *context = NULL;
static struct lws_client_connect_info conn_info = { 0 };
static struct lws *wsi = NULL;

//创建客户端对象（传入服务器地址和端口）
char *server_address = "ip.xiaoice.com";
int port = 80;

static int interrupted;
static int send_type = 0;
#define SEND_TYPE_FILE 0
#define SEND_TYPE_RECORD 1
#define SEND_TYPE_SEND_FILE 2

//audio 参数
int size;
snd_pcm_t * handle;
snd_pcm_hw_params_t * params;
snd_pcm_uframes_t frames;
char * buffer;
time_t current_time;

int record_flag = 0;
int record_size = 0;
char *record_buffer = NULL;
static int record_buffer_offset = 0;

extern void http_download_file_and_play(char *AudioUrl);

void sigint_handler(int sig)
{
	interrupted = 1;
}

char* send_text_command() {
    printf("send_audio_command\n");

    cJSON *jsonContentObject = cJSON_CreateObject();
    cJSON_AddItemToObject(jsonContentObject, "productId", cJSON_CreateString("66eaa703b3c870e931262415"));
    cJSON_AddItemToObject(jsonContentObject, "deviceId", cJSON_CreateString("AF224293PSB98EA"));
    cJSON_AddItemToObject(jsonContentObject, "deviceSecret", cJSON_CreateString("3rxFIBhd7Rr-QA6jYMg2I4QLbjplU_kI"));

    //header
    cJSON *jsonTtsConfigObject = cJSON_CreateObject();
    //cJSON_AddItemToObject(jsonTtsConfigObject, "outputType", cJSON_CreateString("AUDIO_DATA"));  //AUDIO_DATA回过来的音频会以base64编码的二进制发送过来
    cJSON_AddItemToObject(jsonTtsConfigObject, "outputType", cJSON_CreateString("OSS_LINK")); //OSS_LINK回过来的是一个audio url

    cJSON *jsonHeaderObject = cJSON_CreateObject();
    cJSON_AddItemToObject(jsonHeaderObject, "name", cJSON_CreateString("chat.text_in"));
    cJSON_AddItemToObject(jsonHeaderObject, "requestId", cJSON_CreateString("1"));
    cJSON_AddItemToObject(jsonHeaderObject, "ttsConfig", jsonTtsConfigObject);


    cJSON *jsonPayloadObject = cJSON_CreateObject();
    cJSON_AddItemToObject(jsonPayloadObject, "text", cJSON_CreateString("你猜我想问啥"));


    cJSON *jsonObject = cJSON_CreateObject();
    cJSON_AddItemToObject(jsonObject, "context", jsonContentObject);
    cJSON_AddItemToObject(jsonObject, "header", jsonHeaderObject);
    cJSON_AddItemToObject(jsonObject, "payload", jsonPayloadObject);

    char *content = cJSON_Print(jsonObject);
    //printf("send_audio_command: %s\n",  content);
    return content;
}


/**
 * 会话上下文对象，结构根据需要自定义
 */
struct session_data {
    int msg_count;
    unsigned char buf[LWS_PRE + MAX_PAYLOAD_SIZE];
    size_t len;
};

struct session_data *session_data_buf = NULL;

static int payloadAudioUrlIndex = 0;
static char payloadAudioUrlArray[2][128] = {0};
void parseAudioTTSResponse(char *audio_response) {
    // 第一步：解析json 字符串
    cJSON *jsonObject = cJSON_Parse(audio_response);
    if(jsonObject == NULL){
        perror("Parse failed!\n");
        return;
    }

    // 第二步：解析header键值对
    cJSON *headerJson =cJSON_GetObjectItem(jsonObject, "header");
    char *content = cJSON_Print(headerJson);
    //printf("%s\n",  content);

    cJSON *headerNameJson =cJSON_GetObjectItem(headerJson, "name");
    content = cJSON_Print(headerNameJson);
    printf("headerName: %s\n",  content);

    cJSON *headerRequestIdJson =cJSON_GetObjectItem(headerJson, "requestId");
    content = cJSON_Print(headerRequestIdJson);
    printf("headerRequestId: %s\n",  content);


    //解析payload键值对
    cJSON *payloadJson =cJSON_GetObjectItem(jsonObject, "payload");
    if (payloadJson) {
        cJSON *payloadTextJson =cJSON_GetObjectItem(payloadJson, "text");
        if (payloadTextJson) {
            content = cJSON_Print(payloadTextJson);
            printf("payloadTextJson: %s\n",  content);
        }

        cJSON *payloadAudioJson =cJSON_GetObjectItem(payloadJson, "audio");

        cJSON *payloadAudioUrlJson =cJSON_GetObjectItem(payloadAudioJson, "audioUrl");
        if (payloadAudioUrlJson) {
            content = cJSON_Print(payloadAudioUrlJson);
            printf("payloadAudioUrlJson: %s\n",  content);

            printf("payloadAudioUrlJson len = %d\n", strlen(content));
            strcpy(payloadAudioUrlArray[payloadAudioUrlIndex], content);
            payloadAudioUrlIndex++;
        }

        cJSON *payloadAudioFormatJson =cJSON_GetObjectItem(payloadAudioJson, "audioFormat");
        if (payloadAudioFormatJson) {
            content = cJSON_Print(payloadAudioFormatJson);
            printf("payloadAudioFormatJson: %s\n",  content);
        }

        cJSON *payloadAudioSampleRateJson =cJSON_GetObjectItem(payloadAudioJson, "sampleRate");
        if (payloadAudioSampleRateJson) {
            content = cJSON_Print(payloadAudioSampleRateJson);
            printf("payloadAudioSampleRateJson: %s\n",  content);
        }
    }
}

/**
 * 某个协议下的连接发生事件时，执行的回调函数
 *
 * wsi：指向WebSocket实例的指针
 * reason：导致回调的事件
 * user 库为每个WebSocket会话分配的内存空间
 * in 某些事件使用此参数，作为传入数据的指针
 * len 某些事件使用此参数，说明传入数据的长度
 */

//为1时表示可写
int lws_client_writeable = 0;
int lws_client_callback( struct lws *wsi, 
                enum lws_callback_reasons reason, 
                void *user, 
                void *in, 
                size_t len )
{
    switch ( reason ) {

        case LWS_CALLBACK_CLIENT_ESTABLISHED:   // 连接到服务器后的回调
            lwsl_notice( "Connected to server ok!\n" );
            break;
 
        case LWS_CALLBACK_CLIENT_RECEIVE:       // 接收到服务器数据后的回调，数据为in，其长度为len
            lwsl_notice("len = %d\n", (int)len);
            printf( "RxLength: %d,  Rx: %s\n", (int)strlen((char *)in), (char *) in );
            parseAudioTTSResponse((char *)in);
            break;

        case LWS_CALLBACK_CLIENT_WRITEABLE:     // 当此客户端可以发送数据时的回调
            session_data_buf = (struct session_data *) user;
            lws_client_writeable = 1;
            break;

        default:
            break;
    }

    return 0;
}

/**
 * 支持的WebSocket子协议数组
 * 子协议即JavaScript客户端WebSocket(url, protocols)第2参数数组的元素
 * 你需要为每种协议提供回调函数
 */
struct lws_protocols protocols[] = {
    {
        //协议名称，协议回调，接收缓冲区大小
        "ws", lws_client_callback, sizeof( struct session_data ), MAX_PAYLOAD_SIZE, 0, NULL, 0
    },
    {
		// 最后一个元素固定为此格式
        NULL, NULL, 0, 0, 0, NULL, 0
    }
};

/* 连接客户端 */
int connect_server(int is_ssl_support)
{
    char addr_port[256] = { 0 };
    sprintf(addr_port, "%s:%u", server_address, port & 65535 );
 
    // 客户端连接参数
    //conn_info = { 0 };
    conn_info.context = context;
    conn_info.address = server_address;
    conn_info.port = port;

    if(!is_ssl_support)
        conn_info.ssl_connection = 0;
    else
        conn_info.ssl_connection = 1;
    conn_info.path = "./openapi/v1/iot-ws/characters/cjfx-ledi/sessions/testwav123";
    //conn_info.path = "./openapi/v1/iot/device/activate";
    conn_info.host = addr_port;
    conn_info.origin = addr_port;
    conn_info.protocol = protocols[ 0 ].name;
 
    // 下面的调用触发LWS_CALLBACK_PROTOCOL_INIT事件
    // 创建一个客户端连接
    wsi = lws_client_connect_via_info( &conn_info );
    if(!wsi)
        return -1;
    return 1;
}

/*
运行客户端
*/
int run_loop(int wait_time)
{
    lws_service( context, wait_time );
    /**
     * 下面的调用的意义是：当连接可以接受新数据时，触发一次WRITEABLE事件回调
     * 当连接正在后台发送数据时，它不能接受新的数据写入请求，所有WRITEABLE事件回调不会执行
     */
    int ret = lws_callback_on_writable( wsi );
	if (ret < 0) {
		lwsl_notice( "run_loop: ret = %d\n", ret);
        printf( "run_loop: ret = %d\n", ret);
	}

	return ret;
}

void send_audio_end() {
    printf("send_audio_end...\n");
    while(1) {
        if (!lws_client_writeable) {
            continue;
        }

        char audio_end[36] = "__AUDIO_END__";
        int audio_end_len = sprintf( (char*)(session_data_buf->buf + LWS_PRE), "%s", audio_end);
        lws_write( wsi, &session_data_buf->buf[ LWS_PRE ], (size_t)audio_end_len, LWS_WRITE_TEXT );
        lws_client_writeable = 0;
        break;
    }
}

void send_audio_file() {
    FILE * file = NULL;
    //char audio_path[64] = "./file_record.wav";
    // char audio_path[64] = "/tmp/cap_out.wav";
    size_t fileSize = 0;
    size_t readLen = 0;
    char audio_data[5* 4096] = {0};

    file = fopen(record_path,"rb");
    if(file == NULL) {
        printf("fopen failed, record_path = %s\n", record_path);
        return;
    }
    fseek(file,0,SEEK_END);
    fileSize = (size_t)ftell(file);
    fseek(file,0,SEEK_SET);

    printf("audio record_path = %s fileSize = %d\n", record_path, (int)fileSize);
    while (fileSize > 0) {
        if (!lws_client_writeable) {
            continue;
        }

        readLen = fread(audio_data,1,sizeof(audio_data),file);

        memcpy(&session_data_buf->buf[ LWS_PRE ], audio_data, readLen);
        lws_write( wsi, &session_data_buf->buf[ LWS_PRE ], readLen, LWS_WRITE_BINARY );
        lws_client_writeable = 0;

       fileSize -= readLen;
    }
    printf("audio send complete...");
}

int g_request_id = 230;
void send_audio_command() {
    printf("send_audio_command\n");

    cJSON *jsonContentObject = cJSON_CreateObject();
    cJSON_AddItemToObject(jsonContentObject, "productId", cJSON_CreateString("66eaa703b3c870e931262415"));
    cJSON_AddItemToObject(jsonContentObject, "deviceId", cJSON_CreateString("AF224293PSB98EA"));
    cJSON_AddItemToObject(jsonContentObject, "deviceSecret", cJSON_CreateString("3rxFIBhd7Rr-QA6jYMg2I4QLbjplU_kI"));

    //header
    cJSON *jsonTtsConfigObject = cJSON_CreateObject();
    cJSON_AddItemToObject(jsonTtsConfigObject, "outputType", cJSON_CreateString("OSS_LINK")); //OSS_LINK回过来的是一个audio url
    cJSON_AddItemToObject(jsonTtsConfigObject, "audioFormat", cJSON_CreateString("WAV"));

    char request_id_str[8] = {0};
    sprintf(request_id_str, "%d", g_request_id);
    printf("send_audio_command, request_id_str = %s\n", request_id_str);
    cJSON *jsonHeaderObject = cJSON_CreateObject();
    cJSON_AddItemToObject(jsonHeaderObject, "name", cJSON_CreateString("chat.audio_in"));
    cJSON_AddItemToObject(jsonHeaderObject, "requestId", cJSON_CreateString(request_id_str));
    cJSON_AddItemToObject(jsonHeaderObject, "ttsConfig", jsonTtsConfigObject);
    g_request_id++;


    cJSON *jsonPayloadObject = cJSON_CreateObject();
    // cJSON_AddItemToObject(jsonPayloadObject, "audioFormat", cJSON_CreateString("WAV"));
    cJSON_AddItemToObject(jsonPayloadObject, "audioFormat", cJSON_CreateString("WAV"));
    cJSON_AddItemToObject(jsonPayloadObject, "sampleRate", cJSON_CreateString("16k"));

    cJSON *jsonObject = cJSON_CreateObject();
    cJSON_AddItemToObject(jsonObject, "context", jsonContentObject);
    cJSON_AddItemToObject(jsonObject, "header", jsonHeaderObject);
    cJSON_AddItemToObject(jsonObject, "payload", jsonPayloadObject);

    char *content = cJSON_Print(jsonObject);
    printf("send_audio_command: %s\n",  content);

    //发送audio command
    while (1) {
        if (lws_client_writeable) {
            printf("lws_client_writeable, can audio_command..\n");
            memset( session_data_buf->buf, 0, sizeof( session_data_buf->buf ));
            char *msg = (char *) &session_data_buf->buf[ LWS_PRE ];
            session_data_buf->len = (size_t)sprintf( msg, "%s", content);
            printf("msg length: %d\n",  (int)session_data_buf->len);
            //printf("send_audio_command msg: %s\n",  msg);
            lws_write( wsi, &session_data_buf->buf[ LWS_PRE ], session_data_buf->len, LWS_WRITE_TEXT );
            lws_client_writeable = 0;
            break;
        } 
    }
    printf("send_audio_command complete...\n");
}

// download and play once
// #define AUDIO_SEND_SIZE 4096
// void read_record_buffer_send() {
//     char audio_path[64] = "./test3.pcm";
//     int readLenSum = 0;
//     int offset = 0;
//     char audio_data[AUDIO_SEND_SIZE] = {0};
//     FILE * pFile;

//     pFile = fopen(audio_path,"wb");
//     if(pFile == NULL) {
//         printf("fopen failed\n");
//         return;
//     }
//     while(1) {
//         if (!lws_client_writeable) {
//             continue;
//         }

//         if (record_flag) {
//             offset = record_buffer_offset - readLenSum;
//             if (offset < AUDIO_SEND_SIZE) {
//                 continue;
//             }

//             memcpy(&session_data_buf->buf[ LWS_PRE ], record_buffer+readLenSum, AUDIO_SEND_SIZE);
//             lws_write( wsi, &session_data_buf->buf[ LWS_PRE ], AUDIO_SEND_SIZE, LWS_WRITE_BINARY );
//             fwrite(record_buffer+readLenSum, sizeof(char), AUDIO_SEND_SIZE, pFile);
            
//             lws_client_writeable = 0;
//             readLenSum += AUDIO_SEND_SIZE;
//             printf("send record, record_flag = 1,  readLenSum = %d ,record_buffer_offset = %d\n", readLenSum, record_buffer_offset);                     
//         }else {
//             offset = record_buffer_offset - readLenSum;
//             printf("--record_buffer_offset=%d--readLenSum=%d-\n", record_buffer_offset,readLenSum);
            
//             if (offset >= AUDIO_SEND_SIZE) {
//                 memcpy(&session_data_buf->buf[ LWS_PRE ], record_buffer+readLenSum, AUDIO_SEND_SIZE);
//                 lws_write( wsi, &session_data_buf->buf[ LWS_PRE ], AUDIO_SEND_SIZE, LWS_WRITE_BINARY );
//                 fwrite(record_buffer+readLenSum, sizeof(char), AUDIO_SEND_SIZE, pFile);
//                 lws_client_writeable = 0;
//                 readLenSum += AUDIO_SEND_SIZE;
//                 printf("send record, record_flag = 0, offset > AUDIO_SEND_SIZE   readLenSum = %d ,record_buffer_offset = %d\n", readLenSum, record_buffer_offset);                     
//             } else {
//                 if (offset == 0) {
//                     printf("offset is 0, break\n");
//                     break;
//                 }

//                 memcpy(&session_data_buf->buf[ LWS_PRE ], record_buffer+readLenSum, offset);
//                 lws_write( wsi, &session_data_buf->buf[ LWS_PRE ], offset, LWS_WRITE_BINARY );
//                 fwrite(record_buffer+readLenSum, sizeof(char), offset, pFile);
//                 lws_client_writeable = 0;
//                 readLenSum += offset;
//                 printf("send record, record_flag = 0,offset < AUDIO_SEND_SIZE  last readLenSum = %d, record_buffer_offset = %d\n", readLenSum, record_buffer_offset);
//                 break;
//             }
//         }
//     }

//     fclose(pFile);
//     printf("read record and send over\n");
// }

//this function is used to luckfox record
#define POPEN_SIZE 1024
int alsa_arecord_cmd() {
    FILE *fp;
    char output[POPEN_SIZE];
    printf("alsa_arecord_cmd begin...\n");

    char cmd[128] = {0};
    char cmd_arg[64] = "arecord -d 5 -r 16000 -c 2 -f S16_LE -t wav";
    sprintf(cmd, "%s %s", cmd_arg, record_path);

    //fp = popen("arecord -d 5 -r 16000 -c 2 -f S16_LE -t wav file_record.wav", "r");
    fp = popen(cmd, "r");
    if (fp == NULL) {
        fprintf(stderr, "popen failed.\n");
        return -1;
    }

    while (fgets(output, POPEN_SIZE, fp) != NULL) {
        printf("%s", output);
    }

    printf("alsa_arecord_cmd end...\n");
    pclose(fp);
    return 0;
}

void *send_audio_thr_fn(void *arg){
    while (1) {
        printf("send_audio_thr_fn ready to check task \n");
        // if (record_flag == 1) {
        //     printf("already to record\n");
        //     send_audio_command();
        //     read_record_buffer_send();
        //     send_audio_end();
        //     break;
        // }

        if (send_type == SEND_TYPE_SEND_FILE) {
            printf("already to send file\n");
            send_audio_command();
            //alsa_arecord_cmd();
            send_audio_file();
            send_audio_end();   
        }

        if (send_type == SEND_TYPE_FILE) {
            printf("already to send file\n");
            send_audio_command();
            //alsa_arecord_cmd();
            mpi_ai_record();
            send_audio_file();
            send_audio_end();


            //no responose will block here
            while (1) {
                if (payloadAudioUrlIndex >= 1) {
                    http_download_file_and_play(payloadAudioUrlArray[0]);

                    if (payloadAudioUrlIndex >=2) {
                        http_download_file_and_play(payloadAudioUrlArray[1]);
                    }
                    payloadAudioUrlIndex = 0;
                    break;
                }
            }

            printf("send_audio_thr_fn on record and play complete...\n");
            //break;
        }
        
        printf("send_audio_thr_fn task end...\n");
       usleep(5000 * 1000);
    }
}

int websockets_main(char *choose)
{
    signal(SIGINT, sigint_handler);
    int n = 0;
    pthread_t ntid;
    int		err;

    if (strcmp(choose, "file") == 0) {
        send_type = SEND_TYPE_FILE;
    }

    if (strcmp(choose, "send_file") == 0) {
        send_type = SEND_TYPE_SEND_FILE;
    }

    err = pthread_create(&ntid, NULL, send_audio_thr_fn, NULL);
	if (err != 0)
		printf("can't create thread");

    printf("main thread\n");

    //初始化客户端
	ctx_info.port = CONTEXT_PORT_NO_LISTEN;
    ctx_info.iface = NULL;
    ctx_info.protocols = protocols;
    ctx_info.gid = (uid_t)-1;
    ctx_info.uid = (gid_t)-1;

    //创建客户端
	// 创建一个WebSocket处理器
    context = lws_create_context( &ctx_info );
    if(!context)
        return -1;

    //连接服务器（需要ssl连接时传入1，否则传0）
    connect_server(0);

    //客户端运行
    while(n >= 0 && !interrupted)
        n = run_loop(1000);

    //销毁资源
    lws_context_destroy(context);

    return 0;
}